home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / mc / extfs / patchfs < prev    next >
Text File  |  2009-10-25  |  9KB  |  380 lines

  1. #! /usr/bin/perl -w
  2. #
  3. # Written by Adam Byrtek <alpha@debian.org>, 2002
  4. #
  5. # Extfs to handle patches in context and unified diff format.
  6. # Known issues: When name of file to patch is modified during editing, 
  7. # hunk is duplicated on copyin. It is unavoidable.
  8.  
  9. use bytes;
  10. use strict;
  11. use POSIX;
  12. use File::Temp 'tempfile';
  13.  
  14. # standard binaries
  15. my $lzma = 'lzma';
  16. my $bzip = 'bzip2';
  17. my $gzip = 'gzip';
  18. my $fileutil = 'file';
  19.  
  20. # date parsing requires Date::Parse from TimeDate module
  21. my $parsedates = eval 'require Date::Parse';
  22.  
  23. # regular expressions
  24. my $unified_header=qr/^--- .*\n\+\+\+ .*\n@@ .* @@.*\n$/;
  25. my $unified_extract=qr/^--- ([^\s]+).*\n\+\+\+ ([^\s]+)\s*([^\t\n]*)/;
  26. my $unified_contents=qr/^([+\-\\ \n]|@@ .* @@)/;
  27.  
  28. my $context_header=qr/^\*\*\* .*\n--- .*\n\*{15}\n$/;
  29. my $context_extract=qr/^\*\*\* ([^\s]+).*\n--- ([^\s]+)\s*([^\t\n]*)/;
  30. my $context_contents=qr/^([!+\-\\ \n]|-{3} .* -{4}|\*{3} .* \*{4}|\*{15})/;
  31.  
  32. my $ls_extract_id=qr/^[^\s]+\s+[^\s]+\s+([^\s]+)\s+([^\s]+)/;
  33. my $basename=qr|^(.*/)*([^/]+)$|;
  34.  
  35. sub patchfs_canonicalize_path ($) {
  36.   my ($fname) = @_;
  37.   $fname =~ s,/+,/,g;
  38.   $fname =~ s,(^|/)(?:\.?\./)+,$1,;
  39.   return $fname;
  40. }
  41.  
  42. # output unix date in a mc-readable format
  43. sub timef
  44. {
  45.     my @time=localtime($_[0]);
  46.     return sprintf '%02d-%02d-%02d %02d:%02d', $time[4]+1, $time[3],
  47.            $time[5]+1900, $time[2], $time[1];
  48. }
  49.  
  50. # parse given string as a date and return unix time
  51. sub datetime
  52. {
  53.     # in case of problems fall back to 0 in unix time
  54.     # note: str2time interprets some wrong values (eg. " ") as 'today'
  55.     if ($parsedates && defined (my $t=str2time($_[0]))) {
  56.     return timef($t);
  57.     }
  58.     return timef(time);
  59. }
  60.  
  61. # print message on stderr and exit
  62. sub error
  63. {
  64.     print STDERR $_[0], "\n";
  65.     exit 1;
  66. }
  67.  
  68. # (compressed) input
  69. sub myin
  70. {
  71.     my ($qfname)=(quotemeta $_[0]);
  72.  
  73.     $_=`$fileutil $qfname`;
  74.     if (/lzma/) {
  75.     return "$lzma -dc $qfname";
  76.     } elsif (/bzip/) {
  77.     return "$bzip -dc $qfname";
  78.     } elsif (/gzip/) {
  79.     return "$gzip -dc $qfname";
  80.     } else {
  81.     return "cat $qfname";
  82.     }
  83. }
  84.  
  85. # (compressed) output
  86. sub myout
  87. {
  88.     my ($qfname,$append)=(quotemeta $_[0],$_[1]);
  89.     my ($sep) = $append ? '>>' : '>';
  90.  
  91.     $_=`$fileutil $qfname`;
  92.     if (/lzma/) {
  93.     return "$lzma -c $sep $qfname";
  94.     } elsif (/bzip/) {
  95.     return "$bzip -c $sep $qfname";
  96.     } elsif (/gzip/) {
  97.     return "$gzip -c $sep $qfname";
  98.     } else {
  99.     return "cat $sep $qfname";
  100.     }
  101. }
  102.  
  103. # select diff filename conforming with rules found in diff.info
  104. sub diff_filename
  105. {
  106.     my ($fsrc,$fdst)= @_;
  107.     $fsrc = patchfs_canonicalize_path ($fsrc);
  108.     $fdst = patchfs_canonicalize_path ($fdst);
  109.     if (!$fdst && !$fsrc) {
  110.     error 'Index: not yet implemented';
  111.     } elsif (!$fsrc || $fsrc eq '/dev/null') {
  112.     return ($fdst,'PATCH-CREATE/');
  113.     } elsif (!$fdst || $fdst eq '/dev/null') {
  114.     return ($fsrc,'PATCH-REMOVE/');
  115.     } elsif (($fdst eq '/dev/null') && ($fsrc eq '/dev/null')) {
  116.     error 'Malformed diff';
  117.     } else {
  118.     # fewest path name components
  119.     if ($fdst=~s|/|/|g < $fsrc=~s|/|/|g) {
  120.         return ($fdst,'');
  121.     } elsif ($fdst=~s|/|/|g > $fsrc=~s|/|/|g) {
  122.         return ($fsrc,'');
  123.     } else {
  124.         # shorter base name
  125.         if (($fdst=~/$basename/,length $2) < ($fsrc=~/$basename/,length $2)) {
  126.         return ($fdst,'');
  127.         } elsif (($fdst=~/$basename/,length $2) > ($fsrc=~/$basename/,length $2)) {
  128.         return ($fsrc,'');
  129.         } else {
  130.         # shortest names
  131.         if (length $fdst < length $fsrc) {
  132.             return ($fdst,'');
  133.         } else {
  134.             return ($fsrc,'');
  135.         }
  136.         }
  137.     }
  138.     }
  139. }
  140.  
  141. # parse unified or context header
  142. sub parse_header
  143. {
  144.     my ($unified,$context,$buf)=@_;
  145.  
  146.     if ($unified) {
  147.     error "Can't parse unified diff header"
  148.       unless ((($$buf.=<I>).=<I>)=~/$unified_header/);
  149.     return $$buf=~/$unified_extract/;
  150.     } elsif ($context) {
  151.     error "Can't parse context diff header"
  152.       unless ((($$buf.=<I>).=<I>)=~/$context_header/);
  153.     return $$buf=~/$context_extract/;
  154.     }
  155. }
  156.  
  157. # list files affected by patch
  158. sub list
  159. {
  160.     my ($archive)=(quotemeta $_[0]);
  161.     my ($state,$pos,$len,$time);
  162.     my ($f,$fsrc,$fdst,$prefix);
  163.     my ($unified,$context)=(0,0);
  164.  
  165.     # use uid and gid from file
  166.     my ($uid,$gid)=(`ls -l $archive`=~/$ls_extract_id/);
  167.  
  168.     import Date::Parse if ($parsedates);
  169.     
  170.     # state==1 means diff contents, state==0 means comments
  171.     $state=0; $len=0; $f='';
  172.     while (<I>) {
  173.  
  174.     # recognize diff type
  175.     if (!$unified && !$context) {
  176.         $unified=1 if (/^--- /);
  177.         $context=1 if (/^\*\*\* /);
  178.         if (!$unified && !$context) {
  179.         $len+=length;
  180.         next;
  181.         }
  182.     }
  183.  
  184.     if (($unified && /^--- /) || ($context && /^\*\*\* [^\*]*$/)) {
  185.         # start of new file
  186.         if ($state==1) {
  187.         printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $len, datetime($time), $prefix, $f
  188.           if $f;
  189.         $len=0;
  190.         }
  191.         $state=1;
  192.  
  193.         ($fsrc,$fdst,$time)=parse_header($unified,$context,\$_);
  194.         ($f,$prefix)=diff_filename($fsrc,$fdst);
  195.         $f=$f.".diff";
  196.  
  197.     } elsif ($state==1 && (($unified && !/$unified_contents/) || ($context && !/$context_contents/))) {
  198.         # start of comments, end of diff contents
  199.         printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $len, datetime($time), $prefix, $f
  200.           if $f;
  201.         $state=$len=0;
  202.     }
  203.  
  204.     $len+=length;
  205.     }
  206.     printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $len, datetime($time), $prefix, $f
  207.       if ($f && $state==1);
  208. }
  209.  
  210. # extract diff from patch
  211. sub copyout
  212. {
  213.     my ($file,$out)=@_;
  214.     my ($fsrc,$fdst,$found,$state,$buf);
  215.     my ($unified,$context)=(0,0);
  216.  
  217.     $file=~s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/;
  218.     $file = patchfs_canonicalize_path ($file);
  219.     
  220.     # state==1 means diff contents, state==0 mens comments
  221.     $state=0; $found=0; $buf='';
  222.     while (<I>) {
  223.  
  224.     # recognize diff type
  225.     if (!$unified && !$context) {
  226.         $unified=1 if (/^--- /);
  227.         $context=1 if (/^\*\*\* /);
  228.         if (!$unified && !$context) {
  229.         $buf.=$_;
  230.         next;
  231.         }
  232.     }
  233.  
  234.     if (($unified && /^--- /) || ($context && /^\*\*\* [^\*]*$/)) {
  235.         last if ($state==1 && $found);
  236.         $state=1;
  237.  
  238.         ($fsrc,$fdst,)=parse_header($unified,$context,\$_);
  239.         $fsrc = patchfs_canonicalize_path ($fsrc);
  240.         $fdst = patchfs_canonicalize_path ($fdst);
  241.         $found=1 if (($fsrc eq $file) || ($fdst eq $file));
  242.  
  243.     } elsif ($state==1 && (($unified && !/$unified_contents/) || ($context && !/$context_contents/))) {
  244.         # start of comments, end of diff contents
  245.         last if ($found);
  246.         $state=0;
  247.         $buf='';
  248.     }
  249.  
  250.     $buf.=$_ if ($found || $state==0)
  251.     }
  252.     if ($found) {
  253.     open O, "> $out";
  254.     print O $buf;
  255.     close O;
  256.     }
  257. }
  258.  
  259. # remove diff(s) from patch
  260. sub rm
  261. {
  262.     my ($archive)=(shift);
  263.     my ($fsrc,$fdst,$found,$state,$buf);
  264.     my ($tmp,$tmpname)=tempfile();
  265.     my ($unified,$context)=(0,0);
  266.  
  267.     @_=map {scalar(s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/,$_)} @_;
  268.  
  269.     # state==1 means diff contents, state==0 mens comments
  270.     $state=0; $found=0; $buf='';
  271.     while (<I>) {
  272.  
  273.     # recognize diff type
  274.     if (!$unified && !$context) {
  275.         $unified=1 if (/^--- /);
  276.         $context=1 if (/^\*\*\* /);
  277.         if (!$unified && !$context) {
  278.         $buf.=$_;
  279.         next;
  280.         }
  281.     }
  282.  
  283.     if (($unified && /^--- /) || ($context && /^\*\*\* [^\*]*$/)) {
  284.         $state=1;
  285.  
  286.         ($fsrc,$fdst,)=parse_header($unified,$context,\$_);
  287.  
  288.         # remove listed files
  289.         foreach (@_) {
  290.         if (($fsrc eq $_) || ($fdst eq $_)) {
  291.             $found=1;
  292.             last;
  293.         }
  294.         }
  295.         if (!$found) {
  296.         print $tmp $buf;
  297.         $buf='';
  298.         }
  299.  
  300.     } elsif ($state==1 && (($unified && !/$unified_contents/) || ($context && !/$context_contents/))) {
  301.         # start of comments, end of diff contents
  302.         $found=0;
  303.         $state=0;
  304.         $buf='';
  305.     }
  306.  
  307.     if ($state==0) {
  308.         $buf.=$_;
  309.     } elsif (!$found) {
  310.         print $tmp $_;
  311.     }
  312.     }
  313.     print $tmp $buf if (!$found);
  314.     close $tmp;
  315.     close I;
  316.  
  317.     # replace archive with temporary file
  318.     system('cat '.quotemeta($tmpname).'|'.myout($archive,0))==0
  319.       or error "Can't write to archive";
  320.     system 'rm -f '.quotemeta($tmpname);
  321. }
  322.  
  323. # append diff to archive
  324. sub copyin
  325. {
  326.     my ($archive,$name,$src)=(@_);
  327.     my ($fsrc,$fdst,$f,@files);
  328.     my ($unified,$context)=(0,0);
  329.  
  330.     # build filelist
  331.     open I, myin($src).'|';
  332.     while (<I>) {
  333.     # recognize diff type
  334.     if (!$unified && !$context) {
  335.         $unified=1 if (/^--- /);
  336.         $context=1 if (/^\*\*\* /);
  337.     }
  338.  
  339.     if (($unified && /^--- /) || ($context && /^\*\*\* [^\*]*$/)) {
  340.         ($fsrc,$fdst,)=parse_header($unified,$context,\$_);
  341.         ($f,)=diff_filename($fsrc,$fdst);
  342.         push(@files,$f);
  343.     }
  344.     }
  345.     close I;
  346.  
  347.     # remove overwrited files
  348.     open I, myin($archive).'|';
  349.     rm ($archive, map($_.'.diff',@files));
  350.     close I;
  351.  
  352.     my $cmd1=myin($src);
  353.     my $cmd2=myout($archive,1);
  354.     system("$cmd1 | $cmd2")==0
  355.       or error "Can't write to archive";
  356. }
  357.  
  358.  
  359. if ($ARGV[0] eq 'list') {
  360.     open I, myin($ARGV[1]).'|';
  361.     list $ARGV[1];
  362.     exit 0;
  363. } elsif ($ARGV[0] eq 'copyout') {
  364.     open I, myin($ARGV[1])."|";
  365.     copyout ($ARGV[2], $ARGV[3]);
  366.     exit 0;
  367. } elsif ($ARGV[0] eq 'rm') {
  368.     open I, myin($ARGV[1])."|";
  369.     rm ($ARGV[1], $ARGV[2]);
  370.     exit 0;
  371. } elsif ($ARGV[0] eq 'rmdir') {
  372.     exit 0;
  373. } elsif ($ARGV[0] eq 'mkdir') {
  374.     exit 0;
  375. } elsif ($ARGV[0] eq 'copyin') {
  376.     copyin ($ARGV[1], $ARGV[2], $ARGV[3]);
  377.     exit 0;
  378. }
  379. exit 1;
  380.